home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / proc / procTable.c < prev    next >
C/C++ Source or Header  |  1991-05-16  |  16KB  |  650 lines

  1. /* 
  2.  * procTable.c --
  3.  *
  4.  *    Routines to manage the process table.  This maintains a monitor
  5.  *    that synchronizes access to PCB's.
  6.  *
  7.  * Copyright 1985, 1988 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /sprite/src/kernel/proc/RCS/procTable.c,v 9.12 91/05/16 14:06:39 kupfer Exp $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. #include <sprite.h>
  22. #include <mach.h>
  23. #include <proc.h>
  24. #include <procInt.h>
  25. #include <sync.h>
  26. #include <sched.h>
  27. #include <timer.h>
  28. #include <list.h>
  29. #include <vm.h>
  30. #include <sys.h>
  31. #include <stdlib.h>
  32. #include <rpc.h>
  33.  
  34. Sync_Lock    tableLock;
  35. #define    LOCKPTR &tableLock
  36.  
  37. static Proc_ControlBlock  *RunningProcesses[MACH_MAX_NUM_PROCESSORS];
  38. Proc_ControlBlock  **proc_RunningProcesses = RunningProcesses;
  39. Proc_ControlBlock **proc_PCBTable;
  40.  
  41. #define PROC_MAX_PROCESSES 256
  42. #define PROC_PCB_NUM_ALLOC 16
  43. int proc_MaxNumProcesses;
  44.  
  45. int procLastSlot = 0;    /* Circular index into proctable for choosing slots */
  46. static int realMaxProcesses;    /* The absolute number of process table
  47.                  * entries, not necessarily allocated yet. */
  48. static int entriesInUse = 0;    /* Number of PCB's in use. */
  49.  
  50. static void     InitPCB _ARGS_((Proc_ControlBlock *pcbPtr, int i));
  51. static void    AddPCBs _ARGS_((Proc_ControlBlock **procPtrPtr));
  52.  
  53.  
  54. /*
  55.  * ----------------------------------------------------------------------------
  56.  *
  57.  * ProcInitTable --
  58.  *
  59.  *    Initializes the PCB table and running process table.  Must be called
  60.  *    at initialization time with interrupts off.  Initializes an array
  61.  *    of PROC_MAX_PROCESSES pointers to PCB's but only allocates
  62.  *    PROC_PCB_NUM_ALLOC entries at first.  The rest are done dynamically.
  63.  *
  64.  * Results:
  65.  *    None.
  66.  *
  67.  * Side effects:
  68.  *    The PCB table is initialized.
  69.  *
  70.  * ----------------------------------------------------------------------------
  71.  */
  72.  
  73. void
  74. ProcInitTable()
  75. {
  76.     register    int           i;
  77.     register    Proc_ControlBlock *pcbPtr;
  78.     int     maxRunningProcesses;
  79.  
  80.     maxRunningProcesses = MACH_MAX_NUM_PROCESSORS;
  81.     proc_MaxNumProcesses     = PROC_PCB_NUM_ALLOC;
  82.     realMaxProcesses         = PROC_MAX_PROCESSES;
  83.  
  84.     proc_PCBTable = (Proc_ControlBlock **)
  85.         Vm_BootAlloc(realMaxProcesses * sizeof(pcbPtr));
  86.  
  87.     for (i = 0; i < proc_MaxNumProcesses; i++) {
  88.     pcbPtr = (Proc_ControlBlock *) Vm_BootAlloc(sizeof(Proc_ControlBlock));
  89.     proc_PCBTable[i] = pcbPtr;
  90.     InitPCB(pcbPtr, i);
  91.     }
  92.  
  93.     /*
  94.      * Set the rest of the proc table to catch any misuse of nonexistent
  95.      * entries.
  96.      */
  97.  
  98.     for (i = proc_MaxNumProcesses; i < realMaxProcesses; i++) {
  99.     proc_PCBTable[i] = (Proc_ControlBlock *) NIL;
  100.     }
  101.  
  102.     for (i = 0; i < maxRunningProcesses; i++) {
  103.         proc_RunningProcesses[i] = (Proc_ControlBlock *) NIL;
  104.     }
  105.     Sync_LockInitDynamic(&tableLock, "Proc:tableLock");
  106. }
  107.  
  108.  
  109. /*
  110.  * ----------------------------------------------------------------------------
  111.  *
  112.  * InitPCB --
  113.  *
  114.  *    Initializes a process control block.
  115.  *
  116.  * Results:
  117.  *    None.
  118.  *
  119.  * Side effects:
  120.  *    None.
  121.  *
  122.  * ----------------------------------------------------------------------------
  123.  */
  124. static void
  125. InitPCB(pcbPtr, i)
  126.     Proc_ControlBlock *pcbPtr;
  127.     int i;
  128. {
  129.     List_InitElement((List_Links *)pcbPtr);
  130.     pcbPtr->state        = PROC_UNUSED;
  131.     pcbPtr->processID    = i;
  132.     pcbPtr->genFlags    = 0;
  133.  
  134.     /*
  135.      *  Initialize the pointers to the list headers and the
  136.      *  PCB entry. These values do not change when the PCB
  137.      *  entry is re-used.
  138.      */
  139.     pcbPtr->childList    = &(pcbPtr->childListHdr);
  140.     pcbPtr->siblingElement.procPtr    = pcbPtr;
  141.     pcbPtr->familyElement.procPtr    = pcbPtr;
  142.  
  143.     /*
  144.      *  Set the links to NIL to catch any invalid uses of
  145.      *  the lists before they are properly initialized.
  146.      *  These pointers change whenever the PCB entry is re-used.
  147.      */
  148.     pcbPtr->childListHdr.nextPtr    = (List_Links *) NIL;
  149.     pcbPtr->childListHdr.prevPtr    = (List_Links *) NIL;
  150.  
  151.     List_InitElement((List_Links *)&pcbPtr->siblingElement);
  152.     List_InitElement((List_Links *)&pcbPtr->familyElement);
  153.  
  154.     pcbPtr->eventHashChain.procPtr = pcbPtr;
  155.     List_InitElement((List_Links *)&pcbPtr->eventHashChain);
  156.     pcbPtr->event = NIL;
  157.  
  158.     pcbPtr->peerHostID = NIL;
  159.     pcbPtr->peerProcessID = (Proc_PID) NIL;
  160.     pcbPtr->remoteExecBuffer = (Address) NIL;
  161.     pcbPtr->migCmdBuffer = (Address) NIL;
  162.     pcbPtr->migCmdBufSize = 0;
  163.     pcbPtr->migFlags = 0;
  164.     pcbPtr->argString = (char *) NIL;
  165. #ifdef LOCKDEP
  166.     pcbPtr->lockStackSize = 0;
  167. #endif
  168.     pcbPtr->locksHeld = 0;
  169.     pcbPtr->vmPtr = (Vm_ProcInfo *)NIL;
  170.     pcbPtr->fsPtr = (Fs_ProcessState *)NIL;
  171.     pcbPtr->rpcClientProcess = (Proc_ControlBlock *) NIL;
  172.  
  173.     pcbPtr->waitToken = 0;
  174.     pcbPtr->timerArray = (struct ProcIntTimerInfo *) NIL;
  175.  
  176.     pcbPtr->kcallTable = mach_NormalHandlers;
  177.     pcbPtr->specialHandling = 0;
  178.     pcbPtr->machStatePtr = (struct Mach_State *)NIL;
  179. #ifndef CLEAN_LOCK
  180.     Sync_SemInitDynamic(&pcbPtr->lockInfo, "Proc:perPCBlock");
  181. #endif
  182. #ifdef LOCKREG
  183.     Sync_LockRegister(&pcbPtr->lockInfo);
  184. #endif
  185. }
  186.  
  187.  
  188. /*
  189.  * ----------------------------------------------------------------------------
  190.  *
  191.  *  AddPCBs --
  192.  *
  193.  *    Add new proc_ControlBlocks with sched_Mutex locked.  This avoids
  194.  *    conflicts accessing the proc_MaxNumProcesses variable, such as in
  195.  *    the Sched_ForgetUsage routine.
  196.  *
  197.  * Results:
  198.  *    None.
  199.  *
  200.  * Side effects:
  201.  *    The global array of process control blocks is updated to point
  202.  *    to the PCB's pointed to by procPtrPtr, and the count of useable entries
  203.  *    is updated.
  204.  *
  205.  * ----------------------------------------------------------------------------
  206.  */
  207.  
  208. static void
  209. AddPCBs(procPtrPtr)
  210.     Proc_ControlBlock **procPtrPtr;
  211. {
  212.     register int i;
  213.     
  214.     /*
  215.      *  Gain exclusive access to the process table.
  216.      */
  217.     MASTER_LOCK(sched_MutexPtr);
  218.  
  219.     for (i = 0; i < PROC_PCB_NUM_ALLOC; i++) {
  220.     proc_PCBTable[proc_MaxNumProcesses] = *procPtrPtr;
  221.     procPtrPtr++;
  222.     proc_MaxNumProcesses++;
  223.     }
  224.  
  225.     MASTER_UNLOCK(sched_MutexPtr);
  226. }
  227.     
  228.  
  229. /*
  230.  * ----------------------------------------------------------------------------
  231.  *
  232.  * Proc_InitMainProc --
  233.  *
  234.  *    Finish initializing the process table by making a proc table entry
  235.  *    for the main process.  Called with interrupts disabled.
  236.  *
  237.  * Results:
  238.  *    None.
  239.  *
  240.  * Side effects:
  241.  *    The first element of the proc table is modified, and the count of
  242.  *    used entries is set to 1.
  243.  *
  244.  * ----------------------------------------------------------------------------
  245.  */
  246.  
  247. void
  248. Proc_InitMainProc()
  249. {
  250.     register    Proc_ControlBlock *procPtr;
  251.  
  252. #define MAIN_PID 0
  253.  
  254.     entriesInUse = 1;
  255.     
  256.     procPtr = proc_PCBTable[MAIN_PID];
  257.  
  258.     /*
  259.      *  Initialize the main process.
  260.      */
  261.     procPtr->state        = PROC_RUNNING;
  262.     procPtr->genFlags        = PROC_KERNEL;
  263.     procPtr->syncFlags        = 0;
  264.     procPtr->schedFlags        = 0;
  265.     procPtr->processID         = MAIN_PID | (1 << PROC_GEN_NUM_SHIFT) | 
  266.                     (rpc_SpriteID << PROC_ID_NUM_SHIFT);
  267.     procPtr->parentID        = procPtr->processID;
  268.     procPtr->billingRate     = PROC_NORMAL_PRIORITY;
  269.     procPtr->recentUsage     = 0;
  270.     procPtr->weightedUsage     = 0;
  271.     procPtr->unweightedUsage     = 0;
  272.     procPtr->kernelCpuUsage.ticks     = timer_TicksZeroSeconds;
  273.     procPtr->userCpuUsage.ticks       = timer_TicksZeroSeconds;
  274.     procPtr->childKernelCpuUsage.ticks = timer_TicksZeroSeconds;
  275.     procPtr->childUserCpuUsage.ticks  = timer_TicksZeroSeconds;
  276.     procPtr->numQuantumEnds     = 0;
  277.     procPtr->numWaitEvents     = 0;
  278.  
  279.     procPtr->Prof_Buffer        = (short *) NIL;
  280.     procPtr->Prof_BufferSize    = 0;
  281.     procPtr->Prof_Offset        = 0;
  282.     procPtr->Prof_Scale         = 0;
  283.     procPtr->Prof_PC            = 0;
  284.  
  285.     Mach_InitFirstProc(procPtr);
  286.     Vm_ProcInit(procPtr);
  287.     (void) VmMach_SetupContext(procPtr);
  288.  
  289.     procPtr->familyID         = PROC_NO_FAMILY;    /* not in a family */
  290.     
  291.     List_Init(procPtr->childList);
  292.  
  293.     procPtr->userID        = 0;
  294.     procPtr->effectiveUserID    = 0;
  295.  
  296.     Sig_ProcInit(procPtr);
  297.  
  298.     procPtr->processor = Mach_GetProcessorNumber();
  299.     Proc_SetCurrentProc(procPtr);
  300.  
  301.     ProcInitMainEnviron(procPtr);
  302.  
  303.     ProcFamilyHashInit();
  304.  
  305.     procPtr->peerProcessID = (Proc_PID) NIL;
  306.     procPtr->peerHostID = (int) NIL;
  307.     procPtr->remoteExecBuffer = (Address) NIL;
  308. }
  309.  
  310.  
  311. /*
  312.  * ----------------------------------------------------------------------------
  313.  *
  314.  * Proc_LockPID --
  315.  *
  316.  *    Determine the validity of the given pid and if valid return a pointer
  317.  *    to the proc table entry.  The proc table entry is returned locked.
  318.  *
  319.  * Results:
  320.  *    Pointer to proc table entry.
  321.  *
  322.  * Side effects:
  323.  *    Proc table entry is locked.
  324.  *
  325.  * ----------------------------------------------------------------------------
  326.  */
  327.  
  328. ENTRY Proc_ControlBlock *
  329. Proc_LockPID(pid)
  330.     Proc_PID    pid;
  331. {
  332.     register    Proc_ControlBlock *procPtr;
  333. #ifndef CLEAN_LOCK
  334.     register    Sync_Semaphore      *lockPtr;
  335. #endif
  336.  
  337.     LOCK_MONITOR;
  338.  
  339.     if (Proc_PIDToIndex(pid) >= proc_MaxNumProcesses) {
  340.     UNLOCK_MONITOR;
  341.     return((Proc_ControlBlock *) NIL);
  342.     }
  343.     procPtr = proc_PCBTable[Proc_PIDToIndex(pid)];
  344. #ifndef CLEAN_LOCK
  345.     lockPtr = &(procPtr->lockInfo);
  346. #endif
  347.  
  348.     while (TRUE) {
  349.     if (procPtr->state == PROC_UNUSED || procPtr->state == PROC_DEAD) {
  350.         procPtr = (Proc_ControlBlock *) NIL;
  351.         break;
  352.     }
  353.  
  354.     if (procPtr->genFlags & PROC_LOCKED) {
  355.         do {
  356. #ifndef CLEAN_LOCK
  357.         Sync_RecordMiss(lockPtr);
  358. #endif
  359.         (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  360.         } while (procPtr->genFlags & PROC_LOCKED);
  361.     } else {
  362.         if (!Proc_ComparePIDs(procPtr->processID, pid)) {
  363.         procPtr = (Proc_ControlBlock *) NIL;
  364.         } else {
  365.         procPtr->genFlags |= PROC_LOCKED;
  366. #ifndef CLEAN_LOCK
  367.         Sync_RecordHit(lockPtr);
  368.         Sync_StoreDbgInfo(lockPtr, FALSE);
  369.         Sync_AddPrior(lockPtr);
  370. #endif
  371.         }
  372.         break;
  373.     }
  374.     }
  375.  
  376.     UNLOCK_MONITOR;
  377.     return(procPtr);
  378. }
  379.  
  380.  
  381. /*
  382.  * ----------------------------------------------------------------------------
  383.  *
  384.  * Proc_Lock --
  385.  *
  386.  *    Lock the proc table entry.
  387.  *
  388.  * Results:
  389.  *    None.
  390.  *
  391.  * Side effects:
  392.  *    Proc table entry is locked.
  393.  *
  394.  * ----------------------------------------------------------------------------
  395.  */
  396.  
  397. ENTRY void
  398. Proc_Lock(procPtr)
  399.     register    Proc_ControlBlock *procPtr;
  400. {
  401. #ifndef CLEAN_LOCK
  402.     register    Sync_Semaphore      *lockPtr;
  403. #endif
  404.  
  405.     LOCK_MONITOR;
  406.  
  407. #ifndef CLEAN_LOCK
  408.     lockPtr = &(procPtr->lockInfo);
  409. #endif
  410.  
  411.     while (procPtr->genFlags & PROC_LOCKED) {
  412.  
  413. #ifndef CLEAN_LOCK
  414.     Sync_RecordMiss(lockPtr);
  415. #endif
  416.     (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  417.     }
  418.     procPtr->genFlags |= PROC_LOCKED;
  419.  
  420. #ifndef CLEAN_LOCK
  421.     Sync_RecordHit(lockPtr);
  422.     Sync_StoreDbgInfo(lockPtr, FALSE);
  423.     Sync_AddPrior(lockPtr);
  424.     Sync_AddPrior(lockPtr);
  425. #endif
  426.  
  427.     UNLOCK_MONITOR;
  428. }
  429.  
  430.  
  431. /*
  432.  * ----------------------------------------------------------------------------
  433.  *
  434.  * Proc_Unlock --
  435.  *
  436.  *    Unlock the proc table entry.
  437.  *
  438.  * Results:
  439.  *    None.
  440.  *
  441.  * Side effects:
  442.  *    Proc table entry is unlocked.
  443.  *
  444.  * ----------------------------------------------------------------------------
  445.  */
  446.  
  447. ENTRY void
  448. Proc_Unlock(procPtr)
  449.     register    Proc_ControlBlock *procPtr;
  450. {
  451.     LOCK_MONITOR;
  452.  
  453.     if (!(procPtr->genFlags & PROC_LOCKED)) {
  454.     panic("Proc_Unlock: PCB not locked.\n");
  455.     }
  456.     procPtr->genFlags &= ~PROC_LOCKED;
  457.     Sync_Broadcast(&procPtr->lockedCondition);
  458.  
  459.     UNLOCK_MONITOR;
  460. }
  461.  
  462.  
  463. /*
  464.  * ----------------------------------------------------------------------------
  465.  *
  466.  * ProcGetUnusedPCB --
  467.  *
  468.  *    Return the first unused PCB.
  469.  *
  470.  * Results:
  471.  *    Pointer to PCB.
  472.  *
  473.  * Side effects:
  474.  *    Proc table entry is locked and marked as PROC_NEW.
  475.  *
  476.  * ----------------------------------------------------------------------------
  477.  */
  478.  
  479. ENTRY Proc_ControlBlock *
  480. ProcGetUnusedPCB()
  481. {
  482.     register    Proc_ControlBlock     **procPtrPtr;
  483.     register    Proc_ControlBlock     *procPtr;
  484.     Proc_ControlBlock             *pcbArray[PROC_PCB_NUM_ALLOC];
  485.     register    int             i;
  486.     int                    generation;
  487.  
  488.     LOCK_MONITOR;
  489.  
  490.  
  491.  
  492.     /* 
  493.      * See if we need to allocate more process table entries.
  494.      */
  495.     if (entriesInUse == proc_MaxNumProcesses) {
  496.     if (proc_MaxNumProcesses > realMaxProcesses - PROC_PCB_NUM_ALLOC) {
  497.         panic("ProcGetUnusedPCB: PCB table full!!\n");
  498.     }
  499.     for (i = 0; i < PROC_PCB_NUM_ALLOC; i++) {
  500.         pcbArray[i] = (Proc_ControlBlock *)
  501.             Vm_RawAlloc(sizeof(Proc_ControlBlock));
  502.         InitPCB(pcbArray[i], proc_MaxNumProcesses + i);
  503.     }
  504.     AddPCBs(pcbArray);
  505.     }
  506.  
  507.     
  508.     
  509.     /*
  510.      * Scan the proc table looking for an unused slot.  The search is
  511.      * circular, starting just after the last slot chosen.  This is done
  512.      * so that slots are not re-used often so the generation number of
  513.      * each slot can just be a few bits wide.
  514.      */
  515.     for (i = procLastSlot, procPtrPtr = &proc_PCBTable[procLastSlot]; ; ) {
  516.     if ((*procPtrPtr)->state == PROC_UNUSED) {
  517.         break;
  518.     }
  519.     i++;
  520.     procPtrPtr++;
  521.     if (i >= proc_MaxNumProcesses) {
  522.         i = 0;
  523.         procPtrPtr = &proc_PCBTable[0];
  524.     }
  525.     /*
  526.      * Shouldn't hit this, but check to avoid infinite loop.
  527.      */
  528.     if (i == procLastSlot) {
  529.         panic("ProcGetUnusedPCB: PCB table full!!\n");
  530.     }
  531.     }
  532.  
  533.     procLastSlot = i+1;
  534.     if (procLastSlot >= proc_MaxNumProcesses) {
  535.     procLastSlot = 0;
  536.     }
  537.     procPtr = *procPtrPtr;
  538.     procPtr->genFlags = PROC_LOCKED;
  539.     procPtr->migFlags = 0;
  540.     procPtr->state = PROC_NEW;
  541.     /*
  542.      *  The PCB entry has a generation number that is incremented each time
  543.      *  the entry is re-used. The low-order bits are in index into
  544.      *  the PCB table.
  545.      */
  546.     generation = (procPtr->processID & PROC_GEN_NUM_MASK) >> PROC_GEN_NUM_SHIFT;
  547.     generation += 1;
  548.     generation = (generation << PROC_GEN_NUM_SHIFT) & PROC_GEN_NUM_MASK;
  549.     procPtr->processID = i | generation | (rpc_SpriteID << PROC_ID_NUM_SHIFT);
  550.  
  551.     entriesInUse++;
  552.  
  553.     UNLOCK_MONITOR;
  554.  
  555.     return(procPtr);
  556. }
  557.  
  558.  
  559. /*
  560.  * ----------------------------------------------------------------------------
  561.  *
  562.  * ProcFreePCB --
  563.  *
  564.  *    Mark the given PCB as unused.
  565.  *
  566.  * Results:
  567.  *    None.
  568.  *
  569.  * Side effects:
  570.  *    Proc table entry marked as PROC_UNUSED.
  571.  *
  572.  * ----------------------------------------------------------------------------
  573.  */
  574.  
  575. ENTRY void
  576. ProcFreePCB(procPtr)
  577.     register    Proc_ControlBlock     *procPtr;
  578. {
  579. #ifdef LOCKREG
  580.     register    Sync_Semaphore      *lockPtr;
  581. #endif
  582.  
  583.     LOCK_MONITOR;
  584.  
  585. #ifdef LOCKREG
  586.     lockPtr = &(procPtr->lockInfo);
  587. #endif
  588.  
  589.     while (procPtr->genFlags & PROC_LOCKED) {
  590. #ifdef LOCKREG
  591.     Sync_RecordMiss(lockPtr);
  592. #endif
  593.     (void) Sync_Wait(&procPtr->lockedCondition, FALSE);
  594.     }
  595.     procPtr->state = PROC_UNUSED;
  596.     procPtr->genFlags = 0;
  597.     entriesInUse--;
  598.  
  599. #ifdef LOCKREG
  600.     Sync_RecordHit(lockPtr);
  601. #endif
  602.  
  603.     UNLOCK_MONITOR;
  604. }
  605.  
  606.  
  607. /*
  608.  * ----------------------------------------------------------------------------
  609.  *
  610.  * ProcTableMatch --
  611.  *
  612.  *    Go through the process table and return an array of process
  613.  *    IDs for which the specified function returns TRUE.
  614.  *
  615.  * Results:
  616.  *    The array of PIDs and the number of matches are returned.
  617.  *
  618.  * Side effects:
  619.  *    None.
  620.  *
  621.  * ----------------------------------------------------------------------------
  622.  */
  623.  
  624. ENTRY int
  625. ProcTableMatch(maxPids, booleanFuncPtr, pidArray)
  626.     int maxPids;            /* size of pidArray */
  627.     Boolean (*booleanFuncPtr) _ARGS_((Proc_ControlBlock *pcbPtr));
  628.                     /* function to match */
  629.     Proc_PID *pidArray;            /* array to store results */
  630. {
  631.     Proc_ControlBlock *pcbPtr;
  632.     int i;
  633.     int matched = 0;
  634.     
  635.     LOCK_MONITOR;
  636.  
  637.     for (i = 0; i < proc_MaxNumProcesses && matched < maxPids; i++) {
  638.     pcbPtr = proc_PCBTable[i];
  639.     if (pcbPtr->state == PROC_UNUSED) {
  640.         continue;
  641.     }
  642.     if ((*booleanFuncPtr)(pcbPtr)) {
  643.         pidArray[matched] = pcbPtr->processID;
  644.         matched++;
  645.     }
  646.     }
  647.     UNLOCK_MONITOR;
  648.     return(matched);
  649. }
  650.